# Создать обработчик исключений, имитирующий «виртуальную память» для любого «запрещённого» адреса — такого, чтение или запись машинного слова по которому приводило
# бы к LOAD_ACCESS_FAULT или STORE_ACCESS_FAULT. Исключение — адрес 0x00000000, он не поддерживается. Предлагается использовать таблицу вида «виртуальный» адрес:значение.
# Размер таблицы — 16 таких пар (т. е. 128 байтов). Можно использовать адрес 0 для обозначения пустой ячейки.
# «Виртуальная память» работает только на операциях lw и sw с регистром t0 в качестве приёмника или источника соответственно (другие варианты не проверяются)
# Чтение по любому адресу работает так:
#  Если адрес уже есть в таблице, возвращается хранящееся там значение
#  Если адреса нет в таблице, возвращается 0 
#  Запись по любому адресу работает так:
#  Если адрес уже есть в таблице, меняется его значение
#  Если адреса нет в таблице, но в ней есть свободная ячейка, сохраняется новая пара «виртуальный» адрес:значение
#  Если адреса нет в таблице, и таблица переполнена, не происходит ничего 

.include "macros.inc"

.globl  main
.text
main:   la      t1 handler
        csrw    t1 utvec
        csrsi   ustatus 1
        
loop:   li      a7 5            # Ввод адреса
        ecall
        beqz    a0 done         # Если 0, конец
        andi    t0 a0 3         # Кратен ли 4
        beqz    t0 read         # Если да — чтение
        andi    a0 a0 -4        # Запись, затираем 2 бита
        mv      t1 a0           # Это «виртуальный» адрес
        li      a7 5            # Вводим значение
        ecall
        mv      t0 a0           # Готовим t0
        sw      t0 (t1)         # Сохраняем значение в «виртуальной памяти»
        b       loop
read:	lw      t0 (a0)         # Читаем значение из «виртуальной памяти»
        mv      a0 t0
        li      a7 1            # Выводим значение
        ecall
        li      a0 '\n'         # И перевод строки
        li      a7 11
        ecall
        b       loop

done:   li      a7 10
        ecall

.data
h_t1:   .space  4
h_t2:   .space  4
h_t3:   .space  4
h_t4:   .space  4
mem:	.space	128
endmem:
.text
handler: 
	csrw    t0 uscratch     	# Сохраним t0
        sw      t1 h_t1 t0      	# Сохраним t1
        sw      t2 h_t2 t0      	# Сохраним t2
        sw      t3 h_t3 t0      	# Сохраним t3        
        sw      t4 h_t4 t0      	# Сохраним t4
                
        csrr    t0 ucause       	# Прочтём причину исключения
        li	t1 5
        bne	t0 t1 checks		# Если это не LOAD_ACCESS_FAULT
	# Чтение по любому адресу работает так:
	#  Если адрес уже есть в таблице, возвращается хранящееся там значение
	#  Если адреса нет в таблице, возвращается 0
       	csrr    t1 utval     		# Получим виртуальный адрес
       	beqz	t1 hdone
       	la	t2 mem
       	la	t3 endmem
rloop:	lw	t0 (t2)
	beq	t0 t1 leqaddr
	addi	t2 t2 8
	bge	t2 t3 lnoaddr
	b rloop
leqaddr:
	addi	t2 t2 4
	lw	t0 (t2)
	b	hdone        
lnoaddr:
	mv	t0 zero
	b	hdone 
               
checks:
	li	t1 7
        bne	t0 t1 anothercall	# Если это не STORE_ACCESS_FAULT
	#  Запись по любому адресу работает так:
	#  Если адрес уже есть в таблице, меняется его значение
	#  Если адреса нет в таблице, но в ней есть свободная ячейка, сохраняется новая пара «виртуальный» адрес:значение
	#  Если адреса нет в таблице, и таблица переполнена, не происходит ничего        
        csrr    t1 utval     	# Получим виртуальный адрес
        beqz	t1 hdone
       	la	t2 mem
       	la	t3 endmem
       	mv	t4 zero		# Сохраним первую свободную ячейку                
sloop:	lw	t0 (t2)
	beq	t0 t1 seqaddr
	bnez	t0 fullcell
	bnez	t4 fullcell
	mv	t4 t2
fullcell:
	addi	t2 t2 8
	bge	t2 t3 snoaddr
	b sloop
snoaddr:
	beqz	t4 hdone
	sw	t1 (t4)
	addi	t4 t4 4
	csrr    t0 uscratch	# Восстановим t0
	sw	t0 (t4)
	b	hdone
seqaddr:
	addi	t2 t2 4
        csrr    t0 uscratch	# Вернем записываемое значение	
	sw	t0 (t2)
	b	hdone        

anothercall:
	csrr    t0 uscratch     # Восстановим t0
        csrsi	ustatus 0	# Запретить обработку исключений (бит 0 в регистре ustatus) 
	b	exit     
        
hdone:  csrr    t1 uepc         # Адрес прерванной инструкции        
        addi    t1 t1 4         # Адрес следующей инструкции
        csrw    t1 uepc         # Запишем его
        
exit:  	lw      t1 h_t1         # Восстановим t1
	lw      t2 h_t2         # Восстановим t2
	lw      t3 h_t3         # Восстановим t3
	lw      t4 h_t4         # Восстановим t4	
        uret
